#include "Arduino.h"
#include <SPI.h>
#include <SPIFFS.h>
#include "SD_MMC.h"

#include "drv_sd_card.h"
#include "pin_config.h"
#include "dd.h"

#define MAX_FILE_SIZE 4096
#define MIN_FREE_SIZE 10240

SD_Card::SD_Card() {
  SD_MMC.setPins(SD_MMC_CLK_PIN, SD_MMC_CMD_PIN, SD_MMC_D0_PIN, SD_MMC_D1_PIN, SD_MMC_D2_PIN, SD_MMC_D3_PIN);
}

SD_Card::~SD_Card() {
  SD_MMC.end();
}

int SD_Card::begin() {
  if (!SD_MMC.begin()) {
    Serial.println("Card Mount Failed");
    return -1;
  }

  _sd_card_type = SD_MMC.cardType();

  if (_sd_card_type == CARD_NONE) {
    Serial.println("No SD_MMC card attached");
    return -2;
  }

  _sd_card_mounted = true;

  _sd_card_type_string.clear();
  if (_sd_card_type == CARD_MMC) {
    _sd_card_type_string = String("MMC");
  }
  else if (_sd_card_type == CARD_SD) {
    _sd_card_type_string = String("SD_MMCSC");
  }
  else if (_sd_card_type == CARD_SDHC) {
    _sd_card_type_string = String("SD_MMCHC");
  }
  else {
    _sd_card_type_string = String("UNKNOWN");
  }

  _sd_card_used_space = (float)SD_MMC.usedBytes() / SD_MMC.totalBytes();

  return 0;
}

size_t SD_Card::get_file_size(const char* filename) {
  if (!_sd_card_mounted) {
    Serial.println("SD_Card not mounted");
    return -1;
  }

  if (!SD_MMC.exists(filename)) {
    Serial.println("File not found");
    return -2;
  }

  File file = SD_MMC.open(filename, FILE_READ);

  if (!file) {
    Serial.println("File open failed");
    return -3;
  }

  return file.size();
}

int SD_Card::read(const char *filename, uint8_t* buffer) {
  if (!_sd_card_mounted) {
    Serial.println("SD_Card not mounted");
    return -1;
  }
  if (!SD_MMC.exists(filename)) {
    Serial.println("File not found");
    return -2;
  }
  File file = SD_MMC.open(filename, FILE_READ);

  if (!file) {
    Serial.println("File open failed");
    return -3;
  }

  size_t file_size = file.size();

  if (file_size >= 4096) {
    Serial.println("File too large");
    return -4;
  }

  uint8_t* read_buffer = (uint8_t*)malloc(file_size);
  size_t read_size = file.read(read_buffer, file_size);
  buffer = read_buffer;

  return read_size;
}

int SD_Card::read(const char *filename, uint8_t* buffer, uint32_t seek, size_t size) {
  if (!_sd_card_mounted) {
    Serial.println("SD_Card not mounted");
    return -1;
  }
  if (!SD_MMC.exists(filename)) {
    Serial.println("File not found");
    return -2;
  }
  File file = SD_MMC.open(filename, FILE_READ);

  if (!file) {
    Serial.println("File open failed");
    return -3;
  }

  size_t file_size = file.size();

  if (seek > file_size) {
    seek = file_size;
  }

  if (file_size < seek + size) {
    size = file_size - seek;
  }
  
  if (size == 0) {
    return 0;
  }

  uint8_t* read_buffer = (uint8_t*)malloc(size);

  file.seek(seek);
  size_t read_size = file.read(read_buffer, size);
  buffer = read_buffer;

  return read_size;
}

int SD_Card::read(const char *filename, uint8_t* buffer, size_t size) {
  return read(filename, buffer, 0, size);
}

int SD_Card::write(const char *filename, const uint8_t* buffer, size_t size) {
  if (!_sd_card_mounted) {
    Serial.println("SD_Card not mounted");
    return -1;
  }
  if (size > MAX_FILE_SIZE) {
    Serial.println("File size too large");
    return -2;
  }

  if (SD_MMC.totalBytes() - SD_MMC.usedBytes() < MIN_FREE_SIZE) {
    Serial.println("Not enough free space on the SD_Card");
    return -3;
  }

  File file = SD_MMC.open(filename, FILE_WRITE);

  if (!file) {
    Serial.println("File open failed");
    return -4;
  }

  file.write(buffer, size);
}

int SD_Card::write(const char *filename, const char* buffer) {
  return write(filename, (uint8_t*)buffer, strlen(buffer));
}

int SD_Card::write(const char *filename, String buffer) {
  return write(filename, (uint8_t*)buffer.c_str(), buffer.length());
}

int SD_Card::append(const char *filename, const uint8_t* buffer, size_t size) {
  if (!_sd_card_mounted) {
    Serial.println("SD_Card not mounted");
    return -1;
  }

  if (size > MAX_FILE_SIZE) {
    Serial.println("File size too large");
    return -2;
  }

  File file = SD_MMC.open(filename, FILE_APPEND);

  if (!file) {
    Serial.println("File open failed");
    return -3;
  }

  file.write(buffer, size);

  return 0;
}

int SD_Card::append(const char *filename, const char* buffer) {
  return append(filename, (uint8_t*)buffer, strlen(buffer));
}

int SD_Card::append(const char *filename, String buffer) {
  return append(filename, (uint8_t*)buffer.c_str(), buffer.length());
}
